#pragma once

#include "OrionFlames.h"
#include "osl/vec2.h"
#include "osl/vec4.h"
#include "ogl/ext.h"
#include "ogl/glsl.h"
#include "ogl/minicam.h"

namespace OrionFlames
{
//Given texture coordinates (TX), output plane coordinates (PL)
class OFAPI CoordMap
{
public:
	static float TexToPlane(float s, float texscalei)
	{
		float u = float(2.0 * (s - 1.0));
		return float(texscalei * u / sqrt(1.0 - (u * u)));
	}

	vec2 TexToPlane(const vec2 &s, float texscalei)
	{
		return vec2(TexToPlane(s.x, texscalei), TexToPlane(s.y, texscalei));
	}

	static float TexToPlaneTan(float s, float texscaleTan = 1.0)
	{
		float c = float(s * M_PI - (0.5 * M_PI));
		float x = tan(c) / texscaleTan;
		return x; //if (s<0.5) return -x; else return +x;
	}

	static vec2 TexToPlanePolar(vec2 s, float texscalei)
	{
		vec2 t = 2.0 * s - vec2(1.0);
		float r = length(t);
		float det = float(1.0 - r * r);
		//if (det<0.0) discard;
		return t * texscalei / sqrt(det);
	}

	//Given plane coordinates (PL), output texture coordinates (TX)
	static float PlaneToTex(float x, float texscale)
	{
		x *= texscale;
		return x / sqrt(1.0f + x * x) * 0.5f + 0.5f;
	}

	static vec2 PlaneToTex(const vec2 &p, float texscale)
	{
		return vec2(PlaneToTex(p.x, texscale), PlaneToTex(p.y, texscale));
	}

	static float PlaneToTexTan(float x, float texscaleTan = 1.0)
	{
		x *= texscaleTan;
		return float(atan(x) * (1.0 / M_PI) + 0.5);
	}

	static vec2 PlaneToTexPolar(vec2 p, float texscale)
	{
		p *= texscale;
		float ir = float(0.5 / sqrt(1 + p.x * p.x + p.y * p.y));
		return ir * p + vec2(0.5);
		//Final output r = ir*length(p)
		//ir = 1/sqrt(k+length^2(p))
		//output r = p/sqrt(k+p^2)
		//r^2 (k+p^2) = p^2
		//r^2 + (r^2-k)p^2 = 0
		//p = sqrt(r^2/(k-r^2)), but then you lose an r from the existing scale
	}

	static string Dump(float texscale, float texscalei)
	{
		string s;
		char ch[512];
		float ot = texscale;
		texscale = 0.5;

		for (double pl = -3.0; pl <= 3.0; pl += 1.0 / 32.0)
		{
			sprintf_s(ch, 512, "%f %f	%f	%f\n", pl, PlaneToTex((float)pl, texscale), PlaneToTexTan((float)pl, texscale), PlaneToTexPolar(vec2(pl,pl), texscale));
			s.append(ch);
		}

		texscale = ot;
		texscalei = (float)(1.0 / ot);
		return s;
	}

	//String for using the functions above in GLSL.
	static string GLSLTexPlaneString()
	{
		if (!m_Initialized)
		{
			m_GLSLTexPlaneString = "//GLSL PLutil code.\n";
			m_GLSLTexPlaneString += "const float M_PI = 3.14159265358979;\n";
			m_GLSLTexPlaneString += "const float M_1_PI = 1.0 / M_PI;\n";
			m_GLSLTexPlaneString += "const float EPS = 1.0e-6;\n";
			m_GLSLTexPlaneString += "uniform float texscale;\n";
			m_GLSLTexPlaneString += "uniform float texscalei;\n\n";

			m_GLSLTexPlaneString += "//Given plane coordinates (PL), output texture coordinates (TX).\n";
			m_GLSLTexPlaneString += "float TXfmPL(float x)\n";
			m_GLSLTexPlaneString += "{\n";
			m_GLSLTexPlaneString += "	x *= texscale;\n";
			m_GLSLTexPlaneString += "	return x / sqrt(1.0 + x * x) * 0.5 + 0.5;\n";
			m_GLSLTexPlaneString += "}\n\n";

			m_GLSLTexPlaneString += "vec2 TXfmPL(vec2 p)\n";
			m_GLSLTexPlaneString += "{\n";
			m_GLSLTexPlaneString += "	return vec2(TXfmPL(p.x), TXfmPL(p.y));\n";
			m_GLSLTexPlaneString += "}\n\n";

			m_GLSLTexPlaneString += "//Given texture coordinate (TX), output plane coordinate (PL).\n";
			m_GLSLTexPlaneString += "float PLfmTX(float s)\n";
			m_GLSLTexPlaneString += "{\n";
			m_GLSLTexPlaneString += "	float u = 2.0 * s - 1.0;\n";
			m_GLSLTexPlaneString += "	return texscalei * u / sqrt(1.0 - u * u);\n";
			m_GLSLTexPlaneString += "}\n\n";

			m_GLSLTexPlaneString += "vec2 PLfmTX(vec2 s)\n";
			m_GLSLTexPlaneString += "{\n";
			m_GLSLTexPlaneString += "	return vec2(PLfmTX(s.x), PLfmTX(s.y));\n";
			m_GLSLTexPlaneString += "}\n\n";
			m_Initialized = true;
		}

		return m_GLSLTexPlaneString;
	}

private:
	static bool m_Initialized;
	static string m_GLSLTexPlaneString;
};
}